/* Time-stamp: <2011-01-20 21:32:06 xinhaoyuan> */
#include "../kernel.h"

static struct gatedesc_t idt[IDT_COUNT];
static struct pseudodesc_t idt_pd = {
     sizeof(idt) - 1, (unsigned long)idt
};

static volatile int      soft_alarm_enabled;
static volatile uint32_t soft_alarm;
static volatile int      soft_alarm_pid;

volatile ex_handler_t	ex_handlers[ISR_COUNT];
volatile irq_handler_t	irq_handlers[IRQ_COUNT];
volatile int			irq_ap[IRQ_COUNT];
volatile kpipe_t       *irq_pipes;

static void send_eoi(int irq)
{
	 if (sysconf.use_pic)
		  pic_send_eoi(irq);
	 if (sysconf.use_lapic)
		  lapic_send_eoi(irq);
}

int
intr_init(void)
{
	 soft_alarm_enabled = 0;

	 uint32_t i;
	 /* Setting up the idt */
     for (i = 0; i != IDT_COUNT; ++i)
		  SET_GATE(idt[i], false, GD_KERN_TEXT, isr[i], 3);
     for (i = 0; i != IRQ_COUNT; ++i)
		  SET_GATE(idt[IRQ_OFFSET + i], false, GD_KERN_TEXT, irq[i], 3);
	 
     /* syscall routine */
     SET_GATE(idt[INT_SYSCALL], false, GD_KERN_TEXT, syscall, 3);
	 SET_GATE(idt[INT_YIELD],   false, GD_KERN_TEXT, int_yield, 3);
     SET_GATE(idt[INT_KCB_RET], false, GD_KERN_TEXT, kcb_ret, 3);
	 SET_GATE(idt[INT_IPI],     false, GD_KERN_TEXT, ipi, 3);

	 /* All interruption would disable further interruption */

     /* flush the idt */
     asm volatile("lidt (%0)" : : "r"(&idt_pd));

     memset((void *)ex_handlers, 0, sizeof(ex_handlers));
     memset((void *)irq_handlers, 0, sizeof(irq_handlers));
	 for (i = 0; i != IRQ_COUNT; ++i)
	 {
		  irq_ap[i] = IPS_OBJ_NULL;
		  kpipe_open(&irq_pipes[i]);
	 }

	 /* Not used only */
     /* register handler function for page fault */
     // register_ex_handler(14, pfhandler);
     /* register monitor for debugging */
     register_ex_handler(3, monitor);

	 if (initial_conf.use_pic)
	 {
	 	  pic_timer_init(PIT_FREQ);
	 	  pic_enable(0);
	 	  pic_enable(IRQ_KBD);
	 }
	 else
	 {
		  pic_timer_init(0);
	 }

	 if (initial_conf.use_ioapic)
	 {
		  /* TODO */
	 }

     return 0;
}

int
intr_init_ap(void)
{
	 /* flush the idt */
	 asm volatile("lidt (%0)" : : "r"(&idt_pd));
}

void
kexception(struct trapframe_t tf)
{
	 kprintf(" got kernel expection %d, error num %04x for pid %d at vaddr 0x%08x\n",
			 tf.num, tf.err, cur_proc_id(), tf.eip);
     if (ex_handlers[tf.num] != NULL) {
		  // kprintf("Incoming TRAP frame at %p\n", &tf);
		  ex_handlers[tf.num](&tf);
	 }
     else
     {
		  int pid = cur_proc_id();
		  uint64_t info = (tf.num & 0xffff) | ((pid & 0xffff) << 16);
		  if (tf.num == EX_PAGEFAULT)
		  {
			   /* PAGE FAULT */
			   info |= (uint64_t)rcr2() << 32;
		  }
		  send_exception(pid, info, WLEVEL_SYSTEM);
	 }
}

void
register_ex_handler(uint32_t n, ex_handler_t h)
{
     ex_handlers[n] = h;
}

int
get_irq_ap(uint32_t irq)
{
	 return irq_ap[irq];
}

void
set_irq_ap(uint32_t irq, int ap)
{
	 irq_ap[irq] = ap;
}

void
kirq(struct trapframe_t tf)
{
	 /* if (envs[cur_env].xinfo.level > 0) */
     /* 	  /\* when in user exception level, do not response irq *\/ */
     /* 	  return ; */
	 
     if (irq_handlers[tf.num] != NULL)
	 {
		  if (irq_handlers[tf.num](&tf) == 0)
			   send_eoi(tf.num);
	 }
	 else if (irq_ap[tf.num] != IPS_OBJ_NULL)
	 {
		  ips_request(irq_ap[tf.num]);
		  send_eoi(tf.num);
	 }
     else
     {
		  cprintf(" got unknown irq request %d\n", tf.num);
		  monitor(NULL);
		  send_eoi(tf.num);
     }
}

/* void */
/* register_irq_handler(uint32_t irq, irq_handler_t h) */
/* { */
/*      irq_handlers[irq] = h; */
/* } */

/* void */
/* register_irq_pipe(uint32_t irq, struct kpipe_t *pipe) */
/* { */
/*      irq_pipes[irq] = pipe; */
/* } */

void
set_soft_timer(uint32_t alarm, int pid)
{
	 soft_alarm = alarm;
	 soft_alarm_pid = pid;
	 soft_alarm_enabled = 1;
}

physaddr_t
st_handler(uint32_t esp, struct trapframe_t tf)
{
	 int lcpu = cur_lcpu();
	 if (lcpu == 0)
	 {
		  ++ sysconf.jiffies_0;
		  if ((sysconf.jiffies_0 == soft_alarm) && soft_alarm_enabled)
		  {
			   soft_alarm_enabled = 0;
			   sch_notify(soft_alarm_pid, WLEVEL_STIMER);
		  }
	 }
	 
	 int _vpt = lcpus[lcpu].vpt;
	 lcpus[lcpu].vpt = 0;

	 physaddr_t result = 0;
	 if (tf.num == 0)
	 {
		  /* irq0 */
		  send_eoi(0);
		  if (irq_handlers[0] != NULL)
		  {
			   irq_handlers[0](&tf);
		  }
		  result = do_schedule(&esp, &tf.eflags, SCH_HINT_PERIOD);
	 }
	 else if (tf.num == INT_YIELD)
	 {
		  /* user yield request */
		  // kprintf("int yield called!\n");
		  result = do_schedule(&esp, &tf.eflags, SCH_HINT_FORCE);
	 }

	 if ((tf.eflags & FL_IF) == 0)
	 {
		  kprintf("Warning: no if flag\n");
	 }
	 
	 lcpus[lcpu].vpt = result == 0 ? _vpt : result;
	 return result;
}

static void
refresh_proc_monitor(struct trapframe_t *tf)
{
	 static char k;
	 int s = "-/|\\"[k = (k + 1) % 4];
	 vga_put_sc(0, s);
	 int i;
	 for (i = 0; i != 16; ++i)
	 {
		  if (procs[i].flags & PFLAG_VALID)
		  {
			   switch (procs[i].sch_status) {
			   case PSCH_RUN:
					s = '>';
					if ((tf->cs | 3) == 0) s |= 0600;
					break;
			   case PSCH_READY:
					s = '*';
					break;
			   case PSCH_WAIT:
					s = '?';
					break;
			   default:
					s = '0' + procs[i].sch_status;
			   }
		  }
		  else
		  {
			   s = 'X';
		  }

		  vga_put_sc(i + 1, s);
	 }
}

void
after_switching_hook(struct trapframe_t tf)
{
	 if (tf.cs == (GD_USER_TEXT | 3))
	 {
		  if ((tf.eflags & FL_IF) == 0)
		  {
			   kprintf("WARNING: no if flag\n");
		  }
		  
		  int pid = cur_proc_id();
		  /* process the scheduler signal */
		  if (procs[pid].signal_processing &&
			  (procs[pid].signal_flags & SF_READY))
		  {
			   procs[pid].signal_flags &= ~SF_READY;
			   /* Call the signal handler */
			   /* push the original return address */
			   tf.stk.esp -= sizeof(uintptr_t);
			   *(uintptr_t *)tf.stk.esp = tf.eip;
			   tf.eip = procs[pid].signal_handler;
		  }
	 }
	 
	 refresh_proc_monitor(&tf);
}

/* Not used anymore */
#if 0
/*
  system handler for page fault.
  
  The handler simply uses system routine to fix the COW(copy on write)
  page, or throws a error message in case when it's really a page
  error.
*/
// #define K_INTR_DEBUG
static void
pfhandler(struct trapframe_t *tf)
{
     void *addr = (void *)rcr2();
     bool handled = false;

	 proc_id_t pid = cur_proc_id();

#ifdef K_INTR_DEBUG
	 kprintf("catch page fault 0x%08x(=%08x) eid = %d, err = %03b {\n",
			 addr, get_vpt(addr), pid, tf->err);
#endif

	 handled = handle_user_pf(addr, tf->err) == 0 ? true : false;
	 
	 /* If the error is still not be fixed, then we throw error.  */
     if (!handled)
     {
		  kprintf(" Page fault in address %08x(=%08x), err num %03b\n",
				  addr, get_vpt(addr), tf->err);
		  monitor(tf);
     }
#ifdef K_INTR_DEBUG
	 else
	 {
		  kprintf("}\n");
	 }
#endif
}
#endif
